#ifndef _WX_THREAD_H_
#define _WX_THREAD_H_

#include "wx/defs.h"

enum wxMutexError {
  wxMUTEX_NO_ERROR = 0,
  wxMUTEX_INVALID,
  wxMUTEX_DEAD_LOCK,
  wxMUTEX_BUSY,
  wxMUTEX_UNLOCKED,
  wxMUTEX_MISC_ERROR
};

enum wxCondError {
  wxCOND_NO_ERROR = 0,
  wxCOND_INVALID,
  wxCOND_TIMEOUT,
  wxCOND_MISC_ERROR
};

enum wxSemaError {
  wxSEMA_NO_ERROR = 0,
  wxSEMA_INVALID,
  wxSEMA_BUSY,
  wxSEMA_TIMEOUT,
  wxSEMA_OVERFLOW,
  wxSEMA_MISC_ERROR
};

enum wxThreadError {
  wxTHREAD_NO_ERROR = 0,
  wxTHREAD_NO_RESOURCE,
  wxTHREAD_RUNNING,
  wxTHREAD_NOT_RUNNING,
  wxTHREAD_KILLED,
  wxTHREAD_MISC_ERROR
};

enum wxThreadKind {
  wxTHREAD_DETACHED,
  wxTHREAD_JOINABLE
};

enum {
  WXTHREAD_MIN_PRIORITY      = 0u,
  WXTHREAD_DEFAULT_PRIORITY  = 50u,
  WXTHREAD_MAX_PRIORITY      = 100u
};

enum wxMutexType {
  wxMUTEX_DEFAULT,
  wxMUTEX_RECURSIVE
};

class wxThreadHelper;
class wxConditionInternal;
class wxMutexInternal;
class wxSemaphoreInternal;
class wxThreadInternal;

class wxMutex {
  public:
    wxMutex( wxMutexType mutexType = wxMUTEX_DEFAULT );
    ~wxMutex();
    bool IsOk() const;
    wxMutexError Lock();
    wxMutexError TryLock();
    wxMutexError Unlock();
  protected:
    wxMutexInternal *m_internal;
    friend class wxConditionInternal;
    DECLARE_NO_COPY_CLASS( wxMutex )
};

class wxMutexLocker {
  public:

    wxMutexLocker( wxMutex& mutex )
      : m_isOk( false ), m_mutex( mutex ) { m_isOk = ( m_mutex.Lock() == wxMUTEX_NO_ERROR ); }
    bool IsOk() const { return m_isOk; }
    ~wxMutexLocker() { if( IsOk() ) m_mutex.Unlock(); }
  private:
    wxMutexLocker( const wxMutexLocker& );
    wxMutexLocker& operator=( const wxMutexLocker& );
    bool     m_isOk;
    wxMutex& m_mutex;
};

#define wxCRITSECT_IS_MUTEX 1
#define wxCRITSECT_INLINE inline

class wxCriticalSection {
  public:
    wxCRITSECT_INLINE wxCriticalSection();
    wxCRITSECT_INLINE ~wxCriticalSection();
    wxCRITSECT_INLINE void Enter();
    wxCRITSECT_INLINE void Leave();
  private:
    wxMutex m_mutex;
    DECLARE_NO_COPY_CLASS( wxCriticalSection )
};

inline wxCriticalSection::wxCriticalSection() { }
inline wxCriticalSection::~wxCriticalSection() { }

inline void wxCriticalSection::Enter() { ( void )m_mutex.Lock(); }
inline void wxCriticalSection::Leave() { ( void )m_mutex.Unlock(); }

#undef wxCRITSECT_INLINE
#undef wxCRITSECT_IS_MUTEX

class wxCriticalSectionLocker {
  public:
    wxCriticalSectionLocker( wxCriticalSection& cs )
      : m_critsect( cs ) {
      m_critsect.Enter();
    }
    ~wxCriticalSectionLocker() {
      m_critsect.Leave();
    }
  private:
    wxCriticalSection& m_critsect;

    DECLARE_NO_COPY_CLASS( wxCriticalSectionLocker )
};

class wxCondition {
  public:
    wxCondition( wxMutex& mutex );
    ~wxCondition();
    bool IsOk() const;
    wxCondError Wait();
    wxCondError WaitTimeout( unsigned long milliseconds );
    wxCondError Signal();
    wxCondError Broadcast();
    wxDEPRECATED( bool Wait( unsigned long milliseconds ) );
  private:
    wxConditionInternal *m_internal;
    DECLARE_NO_COPY_CLASS( wxCondition )
};

inline bool wxCondition::Wait( unsigned long milliseconds ) { return WaitTimeout( milliseconds ) == wxCOND_NO_ERROR; }

class wxSemaphore {
  public:
    wxSemaphore( int initialcount = 0, int maxcount = 0 );
    ~wxSemaphore();
    bool IsOk() const;
    wxSemaError Wait();
    wxSemaError TryWait();
    wxSemaError WaitTimeout( unsigned long milliseconds );
    wxSemaError Post();
  private:
    wxSemaphoreInternal *m_internal;
    DECLARE_NO_COPY_CLASS( wxSemaphore )
};

typedef unsigned long wxThreadIdType;

class wxThread {
  public:

    typedef void *ExitCode;
    static wxThread *This();
    static bool IsMain();
    static void Yield();
    static void Sleep( unsigned long milliseconds );
    static int GetCPUCount();
    static wxThreadIdType GetCurrentId();
    static bool SetConcurrency( size_t level );
    wxThread( wxThreadKind kind = wxTHREAD_DETACHED );
    wxThreadError Create( unsigned int stackSize = 0 );
    wxThreadError Run();
    wxThreadError Delete( ExitCode *rc = ( ExitCode * )NULL );
    ExitCode Wait();
    wxThreadError Kill();
    wxThreadError Pause();
    wxThreadError Resume();
    void SetPriority( unsigned int prio );
    unsigned int GetPriority() const;
    bool IsAlive() const;
    bool IsRunning() const;
    bool IsPaused() const;
    bool IsDetached() const { return m_isDetached; }
    wxThreadIdType GetId() const;
    virtual void OnExit() { }
    virtual bool TestDestroy();
    virtual ~wxThread();
  protected:
    void Exit( ExitCode exitcode = 0 );
    virtual void *Entry() = 0;
  private:
    wxThread( const wxThread& );
    wxThread& operator=( const wxThread& );
    friend class wxThreadInternal;
    wxThreadInternal *m_internal;
    wxCriticalSection m_critsect;
    bool m_isDetached;
};


class wxThreadHelperThread : public wxThread {
  public:
    wxThreadHelperThread( wxThreadHelper& owner ): wxThread( wxTHREAD_JOINABLE ), m_owner( owner ) { }
  protected:
    virtual void *Entry();
  private:
    wxThreadHelper& m_owner;
    wxThreadHelperThread( const wxThreadHelperThread& );
    wxThreadHelperThread& operator=( const wxThreadHelperThread& );
};

class wxThreadHelper {
  private:
    void KillThread() {
      if( m_thread ) {
        m_thread->Kill();
        delete m_thread;
      }
    }

  public:
    wxThreadHelper() : m_thread( NULL ) { }
    virtual ~wxThreadHelper() { KillThread(); }
    wxThreadError Create( unsigned int stackSize = 0 ) {
      KillThread();
      m_thread = new wxThreadHelperThread( *this );
      return m_thread->Create( stackSize );
    }
    virtual void *Entry() = 0;
    wxThread *GetThread() const { return m_thread; }
  protected:
    wxThread *m_thread;
};

inline void *wxThreadHelperThread::Entry() {
  return m_owner.Entry();
}

void wxMutexGuiEnter();
void wxMutexGuiLeave();
inline bool wxIsMainThread() { return wxThread::IsMain(); }

#define wxCRITICAL_SECTION(name) \
  static wxCriticalSection s_cs##name;  \
  wxCriticalSectionLocker cs##name##Locker(s_cs##name)


class wxMutexGuiLocker {
  public:
    wxMutexGuiLocker() { wxMutexGuiEnter(); }
    ~wxMutexGuiLocker() { wxMutexGuiLeave(); }
};

#endif
